Troubleshooting MDX List Items Inside TabItem Causing Build Failure
This report documents a Docusaurus build failure caused by markdown list items (- ) placed inside <TabItem> JSX components across 26 .mdx files.
1) Incident Summary
- Date: 2026-02-18
- Symptom:
build-if-changed.shbuild failed with 26 MDX compilation errors. - Scope: 26 files in
docs/visibility/Seo writing/1.content framework/. - Impact: Production build blocked. Site continued serving the previous build (no downtime), but new content was not deployed.
2) Confirmed Root Cause
Docusaurus MDX v2+ parser treats markdown list items (- ) as listItem nodes. When a </TabItem> JSX closing tag appears after list items inside the same <TabItem>, the parser expects the closing tag to be inside the list context, which is invalid.
Error message (repeated 26 times, one per file):
Error: MDX compilation failed for file "/app/docs/visibility/Seo writing/1.content framework/[filename].mdx"
Cause: Expected the closing tag `</TabItem>` either after the end of `listItem` (line:col)
or another opening tag after the start of `listItem` (line:col)
Why this happened:
- The "AI Collaboration Guidelines" section in each framework file used
<Tabs>with a "Do This"<TabItem>containing markdown bullet lists. - MDX parses
-as alistItemblock element, and the subsequent</TabItem>closing tag gets consumed by the list parser before the JSX parser can close the tag. - This is a known limitation of MDX's interaction between markdown block elements and JSX components.
3) Faulty and Fixed Content
Faulty Pattern
<Tabs>
<TabItem value="dos" label="Do This" default>
- Ask AI to brainstorm subtopics for your core topic
- Use AI for keyword research suggestions
- Have AI generate the cluster map
</TabItem>
</Tabs>
The </TabItem> on the last line is parsed as part of the list item, causing the MDX compilation error.
Applied Fix
<Tabs>
<TabItem value="dos" label="Do This" default>
• Ask AI to brainstorm subtopics for your core topic
• Use AI for keyword research suggestions
• Have AI generate the cluster map
</TabItem>
</Tabs>
Replaced markdown list markers (- ) with HTML bullet entities (• ), which renders identically but is treated as inline text, not a block-level list element.
Affected Files (26 total)
01-how-to-guide-framework.mdx listicle-framework.mdx
beginners-guide-framework.mdx pillar-page-framework.mdx
best-alternatives-framework.mdx pricing-cost-guide-framework.mdx
best-practices-framework.mdx problem-solution-framework.mdx
best-tools-framework.mdx product-review-framework.mdx
buying-guide-framework.mdx pros-and-cons-framework.mdx
case-study-framework.mdx roundup-best-of-framework.mdx
checklist-framework.mdx step-by-step-tutorial-framework.mdx
common-mistakes-framework.mdx templates-swipe-file-framework.mdx
examples-gallery-framework.mdx topic-cluster-framework.mdx
faqs-page-framework.mdx troubleshooting-guide-framework.mdx
for-beginners-explainer-framework.mdx ultimate-guide-framework.mdx
what-is-framework.mdx
x-vs-y-framework.mdx
4) Troubleshooting Steps Performed
# 1) Run build-if-changed.sh — build failed
sudo bash /home/rezriz/github/01-production/docusaurus-build-if-changed/build-if-changed.sh
# 2) Identify all failing files
sudo docker exec -w /app docusaurus npx docusaurus build 2>&1 \
| grep -E "MDX compilation failed" | head -30
# 3) Identify the error pattern (listItem + TabItem conflict)
sudo docker exec -w /app docusaurus npx docusaurus build 2>&1 \
| grep -E "(listItem|TabItem)" | head -30
# 4) Inspect specific file to confirm pattern
grep -n "^- \|TabItem" for-beginners-explainer-framework.mdx
# 5) Apply bulk fix via Python script (convert - to • inside TabItem)
for f in *.mdx; do
python3 -c "
import re
with open('$f', 'r') as fh:
content = fh.read()
lines = content.split('\n')
in_tabitem = False
new_lines = []
for line in lines:
if '<TabItem' in line.strip(): in_tabitem = True
if '</TabItem>' in line.strip(): in_tabitem = False
if in_tabitem and re.match(r'^- ', line):
new_lines.append(line.replace('- ', '• ', 1))
else:
new_lines.append(line)
result = '\n'.join(new_lines)
if result != content:
with open('$f', 'w') as fh: fh.write(result)
print(f'Fixed: $f')
"
done
# 6) Rebuild and verify
sudo bash /home/rezriz/github/01-production/docusaurus-build-if-changed/build-if-changed.sh
5) Verification After Fix
- Build completed successfully:
[SUCCESS] Generated static files in "build".
- Build validation passed:
[build-if-changed] Build validation passed
- Container restarted automatically:
[build-if-changed] Restarting container 'docusaurus'[build-if-changed] Done.
- All 39 framework files now render correctly on the live site.
6) Prevention Checklist
-
Never use markdown list items (
-) inside<TabItem>or other JSX components. Use HTML bullet entities (•),<ul><li>HTML tags, or tables instead. -
MDX JSX + Markdown rule: Block-level markdown elements (lists, blockquotes, headings) inside JSX components can cause parser conflicts. Prefer HTML equivalents inside JSX.
-
Pre-publish build check: Always run the build before assuming content is valid:
sudo docker exec -w /app docusaurus sh -lc "npm run build"
-
Batch content changes: When modifying many MDX files at once, test the build after the batch, not after each individual file.
-
Quick detection command: To find future instances of this pattern:
cd /opt/docker-data/apps/docusaurus/site/docs
grep -rn "^- " --include="*.mdx" | \
while read line; do
file=$(echo "$line" | cut -d: -f1)
grep -q "TabItem" "$file" && echo "$line"
done